package org.javanum.number;

import org.junit.Test;

import static org.javanum.number.TestUtils.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeNotNull;

/**
 * Copyright 2010 Scott Fines
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p/>
 * Author: Scott Fines
 * Date: Mar 8, 2010
 */
public class PolarComplexTestSpecialValues {
    

/*-------------------------------------------------------*/
    /*Addition tests*/

    @Test
    public void testAddComplexInfinity(){
        /*
        Tests that z+Infinity = Infinity+z = Infinity for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeInf = z.add(PolarComplex.COMPLEX_INFINITY);

        assertComplexEquality("z+Infinity!=Infinity",
                PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);

        shouldBeInf = PolarComplex.COMPLEX_INFINITY.add(z);
        assertComplexEquality("Infinity+z!=Infinity",
                        PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);
    }
    @Test
    public void testAddRealInfinity(){
        /*
        Tests that z+PositiveInfinity = PositiveInfinity+z = Infinity for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeInf = z.add(Real.POSITIVE_INFINITY);

        assertComplexEquality("z+PositiveInfinity!=ComplexInfinity",
                PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);

        shouldBeInf = z.add(Real.NEGATIVE_INFINITY);
        assertComplexEquality("z+NegativeInfinity!=ComplexInfinity",
                        PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);

    }

    @Test
    public void testAddComplexNaN() throws Exception {
        /*
        Tests that z+NaN=NaN+z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.add(PolarComplex.NaN);

        assertComplexEquality("z+NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);

        shouldBeNaN = PolarComplex.NaN.add(z);
        assertComplexEquality("NaN+z!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testAddRealNaN() throws Exception {
        /*
        Tests that z+NaN=NaN+z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.add(Real.NaN);

        assertComplexEquality("z+NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testAddComplexZero() throws Exception {
        /*
        Tests that z+0=0+z=z for any PolarComplex z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex sum = z.add(PolarComplex.ZERO);

        assertComplexEquality("z+0!=z",z,sum,DELTA);

        sum = PolarComplex.ZERO.add(z);
        assertComplexEquality("0+z!=z",z,sum,DELTA);
    }

    @Test
    public void testAddRealZero() throws Exception {
        /*
        Tests that z+0=0+z=z for any PolarComplex z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex sum = z.add(Real.ZERO);

        assertComplexEquality("z+0!=z",z,sum,DELTA);
    }

    @Test
    public void testAdditiveInverseZero() throws Exception {
        /*
        Tests that the additive inverse of zero is zero
         */
        PolarComplex z = PolarComplex.ZERO;

        PolarComplex additiveInverse = z.additiveInverse();

        assertComplexEquality("0.additiveInverse()!=0",z,additiveInverse,DELTA);
    }

    /*-------------------------------------------------------*/
    /*Subtraction tests*/
    @Test
    public void testSubtractComplexInfinity(){
        /*
        Tests that z+Infinity = Infinity+z = Infinity for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeInf = z.subtract(PolarComplex.COMPLEX_INFINITY);

        assertComplexEquality("z+Infinity!=Infinity",
                PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);

        shouldBeInf = PolarComplex.COMPLEX_INFINITY.subtract(z);
        assertComplexEquality("Infinity+z!=Infinity",
                        PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);
    }

    @Test
    public void testSubtractRealInfinity(){
        /*
        Tests that z+Infinity = Infinity+z = Infinity for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeInf = z.subtract(Real.POSITIVE_INFINITY);
        assertComplexEquality("z+PositiveInfinity!=ComplexInfinity",
                PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);

        shouldBeInf = z.subtract(Real.NEGATIVE_INFINITY);
        assertComplexEquality("z+NegativeInfinity!=ComplexInfinity",
                        PolarComplex.COMPLEX_INFINITY,shouldBeInf,DELTA);
    }

    @Test
    public void testSubtractComplexNaN() throws Exception {
        /*
        Tests that z+NaN=NaN+z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.subtract(PolarComplex.NaN);

        assertComplexEquality("z+NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);

        shouldBeNaN = PolarComplex.NaN.subtract(z);
        assertComplexEquality("NaN+z!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }
    @Test
    public void testSubtractRealNaN() throws Exception {
        /*
        Tests that z+NaN=NaN+z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.subtract(Real.NaN);

        assertComplexEquality("z-NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testSubtractComplexZero() throws Exception {
        /*
        Tests that z-0=z for any PolarComplex z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex sum = z.subtract(PolarComplex.ZERO);

        assertComplexEquality("z-0!=z",z,sum,DELTA);
    }
    @Test
    public void testSubtractRealZero() throws Exception {
        /*
        Tests that z-0=z for any PolarComplex z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex sum = z.subtract(Real.ZERO);

        assertComplexEquality("z-0!=z",z,sum,DELTA);
    }

/*-------------------------------------------------------*/
    /*Multiplication tests*/
    @Test
    public void testMultiplyComplexInfinity(){
        /*
        Tests that z*Infinity = Infinity*z = Infinity for any
        z not NaN, not 0
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotZero(z);

        PolarComplex shouldBeInf = z.multiply(PolarComplex.COMPLEX_INFINITY);

        assertEquals("z*Infinity!=Infinity",
             PolarComplex.COMPLEX_INFINITY.magnitude,shouldBeInf.magnitude,DELTA);

        shouldBeInf = PolarComplex.COMPLEX_INFINITY.multiply(z);
        assertEquals("Infinity*z!=Infinity",
             PolarComplex.COMPLEX_INFINITY.magnitude,shouldBeInf.magnitude,DELTA);
        
    }

    @Test
    public void testMultiplyRealInfinity(){
        /*
        Tests that z*Infinity = Infinity*z = Infinity for any
        z not NaN, not 0
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotZero(z);

        PolarComplex shouldBeInf = z.multiply(Real.POSITIVE_INFINITY);
        assertEquals("z*PositiveInfinity!=ComplexInfinity",
             PolarComplex.COMPLEX_INFINITY.magnitude,shouldBeInf.magnitude,DELTA);

        shouldBeInf = z.multiply(Real.NEGATIVE_INFINITY);
        assertEquals("z*NegativeInfinity!=ComplexInfinity",
             PolarComplex.COMPLEX_INFINITY.magnitude,shouldBeInf.magnitude,DELTA);

    }


    @Test
    public void testMultiplyComplexNaN() throws Exception {
        /*
        Tests that z*NaN=NaN*z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.multiply(PolarComplex.NaN);

        assertComplexEquality("z*NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);

        shouldBeNaN = PolarComplex.NaN.multiply(z);
        assertComplexEquality("NaN*z!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testMultiplyRealNaN() throws Exception {
        /*
        Tests that z*NaN=NaN*z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.multiply(Real.NaN);

        assertComplexEquality("z*NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testMultiplyComplexZero() throws Exception {
        /*
        Tests that z*0=0 for any PolarComplex z, not NaN, not
        ComplexInfinity
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex mult = z.multiply(PolarComplex.ZERO);
        assertEquals("z*0!=0",0d,mult.magnitude,DELTA);

        mult = PolarComplex.ZERO.multiply(z);
        assertEquals("0*z!=0",0d,mult.magnitude,DELTA);
    }

    @Test
    public void testMultiplyRealZero() throws Exception {
        /*
        Tests that z*0=0 for any PolarComplex z, not NaN, not
        ComplexInfinity
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);
        assumeNotComplexInfinity(z);

        PolarComplex mult = z.multiply(Real.ZERO);
        assertEquals("z*0!=0",0d,mult.magnitude,DELTA);
//        assertComplexEquality("z*0!=0",z,mult,DELTA);
    }

/*-------------------------------------------------------*/
    /*Division tests*/
    @Test
    public void testDivideComplexInfinity(){
        /*
        Tests that z/Infinity=0 for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeZero = z.divide(PolarComplex.COMPLEX_INFINITY);

        assertEquals("z/Infinity!=0",0d,shouldBeZero.magnitude,DELTA);
    }

    @Test
    public void testDivideRealInfinity(){
        /*
        Tests that z/Infinity=0 for any
        z not NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        assumeNotNaN(z);

        PolarComplex shouldBeZero = z.divide(Real.POSITIVE_INFINITY);

        assertMagnitudeEquals("z/PositiveInfinity!=0",
                        PolarComplex.ZERO,shouldBeZero,DELTA);

        shouldBeZero = z.divide(Real.NEGATIVE_INFINITY);
        assertMagnitudeEquals("z/NegativeInfinity!=-0",
                        PolarComplex.ZERO,shouldBeZero,DELTA);
        
    }

    @Test
    public void testDivideComplexNaN() throws Exception {
        /*
        Tests that z/NaN=NaN/z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.divide(PolarComplex.NaN);

        assertComplexEquality("z/NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);

        shouldBeNaN = PolarComplex.NaN.divide(z);
        assertComplexEquality("NaN/z!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test
    public void testDivideRealNaN() throws Exception {
        /*
        Tests that z/NaN=NaN/z = NaN
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);

        PolarComplex shouldBeNaN = z.divide(Real.NaN);

        assertComplexEquality("z/NaN!=NaN",
                PolarComplex.NaN,shouldBeNaN,DELTA);
    }

    @Test(expected=ArithmeticException.class)
    public void testDivideComplexZero() throws Exception {
        /*
        Tests that z/0 throws an exception for non-null z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        z.divide(PolarComplex.ZERO);
    }

    @Test(expected=ArithmeticException.class)
    public void testDivideRealZero() throws Exception {
        /*
        Tests that z/0 throws an exception for non-null z
         */
        PolarComplex z = getOneNumber();
        assumeNotNull(z);
        z.divide(Real.ZERO);
    }

 /*-------------------------------------------------------*/
    /*Modulus tests*/

    @Test
    public void testModulusOfComplexInfinity() throws Exception {
        /*
        Tests that modulus(ComplexInfinity)=Double.POSITIVE_INFINITY
         */
        PolarComplex inf = PolarComplex.COMPLEX_INFINITY;

        double mod = inf.modulus();

        assertEquals("modulus(ComplexInfinity)!=PositiveInfinity",
                Double.POSITIVE_INFINITY,mod,DELTA);
    }

    @Test
    public void testModulusOfNaN() throws Exception {
        /*
        test that modulus(NaN)=Double.NaN
         */
        double mod = PolarComplex.NaN.modulus();

        assertEquals("modulus(NaN)!=NaN",Double.NaN,mod,DELTA);
    }

    @Test
    public void testModulusOfZero() throws Exception {
        /*
        test that modulus(0)=0
         */
        double mod = PolarComplex.ZERO.modulus();

        assertEquals("modulus(0)!=0",0d,mod,DELTA);
    }

/*-------------------------------------------------------*/
    /*Conjugate tests*/

    @Test
    public void testConjugateOfComplexInfinity() throws Exception {
        /*
        Tests that conj(ComplexInfinity)=ComplexInfinity
         */
        PolarComplex conj = PolarComplex.COMPLEX_INFINITY.conjugate();

        assertComplexEquality("conjugate(ComplexInfinity)!=ComplexInfinity",
                PolarComplex.COMPLEX_INFINITY,conj,DELTA);
    }

    @Test
    public void testConjugateOfComplexNaN() throws Exception {
        /*
        Tests that conjugate(NaN)=NaN
         */
        PolarComplex conj = PolarComplex.NaN.conjugate();

        assertComplexEquality("conjugate(NaN)!=NaN",
                        PolarComplex.NaN,conj,DELTA);
    }

    @Test
    public void testConjugateOfZero() throws Exception {
        /*
        Tests that conjugate(0)=0
         */
        PolarComplex conj = PolarComplex.ZERO.conjugate();

        assertComplexEquality("conjugate(0)!=0",
                        PolarComplex.ZERO,conj,DELTA);
    }

/*-------------------------------------------------------*/
    /*Negation tests*/

    @Test
    public void testNegateZero() throws Exception {
        /*
        Tests that negation(0)=0
         */
        PolarComplex neg = PolarComplex.ZERO.negate();

        assertComplexEquality("negation(0)!=0",PolarComplex.ZERO,neg,DELTA);
    }

    @Test
    public void testNegateComplexInfinity() throws Exception {
        /*
        Tests that negation(ComplexInfinity)=ComplexInfinity
         */
        PolarComplex neg = PolarComplex.COMPLEX_INFINITY.negate();

        assertMagnitudeEquals("negation(ComplexInfinity)!=ComplexInfinity",
                PolarComplex.COMPLEX_INFINITY,neg,DELTA);
    }

    @Test
    public void testNegateComplexNaN() throws Exception {
        /*
        Tests that negation(NaN)=NaN
         */
        PolarComplex neg = PolarComplex.NaN.negate();

        assertComplexEquality("negation(ComplexInfinity)!=ComplexInfinity",
                PolarComplex.NaN,neg,DELTA);
    }

    /*-------------------------------------------------------*/
    /*Helper methods*/

    private static PolarComplex getOneNumber() {
        return PolarComplex.valueOf(12,Math.PI/7);
    }
}
